package edu.chalmers.targine;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;

import android.content.Context;
import android.graphics.Paint;
import edu.st.LabelMaker;
import edu.st.NumericSprite;
import edu.st.Projector;

public class TextBillboard {
	public float x, y, z;
	public float xRot, yRot, zRot;
	public float scale;
	private FloatBuffer vertexBuffer;	// buffer holding the vertices
	private FloatBuffer textureBuffer;	// buffer holding the texture coordinates
	
    protected Context mContext;
    private LabelMaker mLabels;
    private Paint mLabelPaint;
    private int mLabelA;
    private Projector mProjector;
    private NumericSprite mNumericSprite;
    
    private boolean _initialised = false;
    
    protected String text = null;
	
	public TextBillboard(Context context, String text, float x, float y, float z, float scale, float xRot){
		mContext = context;
		mProjector = new Projector();
		mLabelPaint = new Paint();
		mLabelPaint.setTextSize(32);
		mLabelPaint.setAntiAlias(true);
		mLabelPaint.setARGB(0xff, 0x00, 0x00, 0x00);
		
		this.x = x;
		this.y = y;
		this.z = z;
		this.scale = scale;
		this.xRot = xRot;
		
		this.text = text;
		
		initBillboard();
	}
	
	private void initBillboard(){
		float vertices[] = {
				-1.0f, -1.0f,  0.0f,		// V1 - bottom left
				-1.0f,  1.0f,  0.0f,		// V2 - top left
				 1.0f, -1.0f,  0.0f,		// V3 - bottom right
				 1.0f,  1.0f,  0.0f			// V4 - top right
		};

		float texture[] = {
				// Mapping coordinates for the vertices OBS flipped for text
				1.0f, 1.0f,		// top right	(V4)
				1.0f, 0.0f,		// bottom right	(V3)
				0.0f, 1.0f,		// top left		(V2)
				0.0f, 0.0f		// bottom left	(V1)
				
		};
		
		// a float has 4 bytes so we allocate for each coordinate 4 bytes
		ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
		byteBuffer.order(ByteOrder.nativeOrder());
		vertexBuffer = byteBuffer.asFloatBuffer();
		vertexBuffer.put(vertices);
		vertexBuffer.position(0);

		byteBuffer = ByteBuffer.allocateDirect(texture.length * 4);
		byteBuffer.order(ByteOrder.nativeOrder());
		textureBuffer = byteBuffer.asFloatBuffer();
		textureBuffer.put(texture);
		textureBuffer.position(0);
	}
	
	public void init(GL10 gl){
		spriteTextSurfaceCreated(gl, null, text);
		spriteTextSurfaceChanged(gl, 1280, 720);
		_initialised = true;
	}
	
	public void draw(GL10 gl){
		float[] modelview = new float[16];
		int i, j;
		
		// save the current modelview matrix
		gl.glPushMatrix();
		// get the current modelview matrix
		((GL11)gl).glGetFloatv(GL11.GL_MODELVIEW_MATRIX, modelview, 0);
		// undo all rotations, beware all scaling is lost as well 
		for( i=0; i<3; i++ ){
			for( j=0; j<3; j++ ){
				if ( i==j ){
					modelview[i*4+j] = 1.0f;
				} else{
					modelview[i*4+j] = 0.0f;
				}
			}
		}
		// set the modelview with no rotations and scaling
		gl.glLoadMatrixf(modelview, 0);
		
		gl.glTranslatef(x, y, z);
		gl.glScalef(scale, scale, scale);
		gl.glRotatef(xRot, 0, 0, 1);

		//enable png transparency
        gl.glEnable(GL10.GL_BLEND);
//        gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
        gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
		/** The draw method for the square with the GL context */
		gl.glEnable(GL10.GL_TEXTURE_2D);
		// bind the previously generated texture
		gl.glBindTexture(GL10.GL_TEXTURE_2D, mLabels.mTextureID);
				
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

		// Point to our vertex buffer
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
		gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);

		// Draw the vertices as triangle strip
		gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);

		//Disable the client state before leaving
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
		gl.glDisable(GL10.GL_BLEND);
		
		// restores the modelview matrix
		gl.glPopMatrix();
	}
	
	public void setText(String text){
		this.text = text;
		_initialised = false;
	}
		
	public void spriteTextSurfaceCreated(GL10 gl, EGLConfig config, String text) {
        /*
         * By default, OpenGL enables features that improve quality
         * but reduce performance. One might want to tweak that
         * especially on software renderer.
         */
//			gl.glDisable(GL10.GL_DITHER);

        /*
         * Some one-time OpenGL initialization can be made here
         * probably based on features of this particular context
         */
//	        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);

//	        gl.glClearColor(.5f, .5f, .5f, 1);
//	        gl.glShadeModel(GL10.GL_SMOOTH);
//	        gl.glEnable(GL10.GL_DEPTH_TEST);
//	        gl.glEnable(GL10.GL_TEXTURE_2D);

        /*
         * Create our texture. This has to be done each time the
         * surface is created.
         */

        int[] textures = new int[1];
        gl.glGenTextures(1, textures, 0);

//        mTextureID = textures[0];
//        gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);

        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);

        gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_REPLACE);

//	        InputStream is = mContext.getResources().openRawResource(R.raw.card_bd);
//	        Bitmap bitmap;
//	        try{
//	            bitmap = BitmapFactory.decodeStream(is);
//	        } finally{
//	            try{
//	                is.close();
//	            } catch(IOException e){
//	                // Ignore.
//	            }
//	        }
//
//	        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
//	        bitmap.recycle();

        if(mLabels != null){
            mLabels.shutdown(gl);
        } else{
            mLabels = new LabelMaker(true, 512, 128);
        }
        mLabels.initialize(gl);
        mLabels.beginAdding(gl);
        setLabel(mLabels.add(gl, text, mLabelPaint));
        mLabels.endAdding(gl);

        if(mNumericSprite != null){
            mNumericSprite.shutdown(gl);
        } else{
            mNumericSprite = new NumericSprite();
        }
        mNumericSprite.initialize(gl, mLabelPaint);
    }
		
	public void spriteTextSurfaceChanged(GL10 gl, int w, int h) {
        gl.glViewport(0, 0, w, h);
        mProjector.setCurrentView(0, 0, w, h);

        /*
        * Set our projection matrix. This doesn't have to be done
        * each time we draw, but usually a new projection needs to
        * be set when the viewport is resized.
        */

        float ratio = (float) w / h;
        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
        mProjector.getCurrentProjection(gl);
    }
	
	public boolean isUninitialised(){
		return !_initialised;
	}

	public int getLabel() {
		return mLabelA;
	}

	public void setLabel(int mLabelA) {
		this.mLabelA = mLabelA;
	}
}
